package com.jwds.digital.bios.business.service.service.belt.impl;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jwds.digital.bios.business.core.constants.BeltCloudConstant;
import com.jwds.digital.bios.business.core.infrastructure.dto.belt.FiveDefenseCount;
import com.jwds.digital.bios.business.core.infrastructure.dto.feign.BasicDeviceReqDTO;
import com.jwds.digital.bios.business.core.infrastructure.dto.feign.FlowBasicReqDTO;
import com.jwds.digital.bios.business.core.infrastructure.entity.belt.*;
import com.jwds.digital.bios.business.core.infrastructure.telegram.BeltRouteBelongData;
import com.jwds.digital.bios.business.core.infrastructure.vo.feign.BasicDeviceRespVO;
import com.jwds.digital.bios.business.core.infrastructure.vo.feign.FlowBasicRespVO;
import com.jwds.digital.bios.business.rpc.feign.BasicDeviceFeign;
import com.jwds.digital.bios.business.rpc.feign.FlowBasicFeign;
import com.jwds.digital.bios.business.service.service.belt.*;
import com.jwds.digital.bios.business.core.infrastructure.dto.belt.BeltAnomalyInfoReqDTO;
import com.jwds.digital.bios.business.core.infrastructure.vo.belt.BeltAnomalyInfoRespVO;
import com.jwds.digital.bios.business.core.infrastructure.dto.belt.assembler.BeltAnomalyInfoAssembler;
import com.jwds.digital.bios.business.core.infrastructure.mapper.belt.BeltAnomalyInfoMapper;
import com.jwds.digital.bios.business.core.exceptions.RecordNotFoundException;
import com.jwds.tdc.framework.application.lib.BaseResponse;
import com.jwds.tdc.framework.application.lib.page.PageRequest;
import com.jwds.tdc.framework.application.lib.page.PageResponse;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;

import com.jwds.tdc.framework.cache.service.CacheService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * 异常信息实现类
 *
 * @author 林柏云
 * @date 2024-10-24
 */
@Slf4j
@Service
public class BeltAnomalyInfoServiceImpl extends ServiceImpl<BeltAnomalyInfoMapper, BeltAnomalyInfo> implements IBeltAnomalyInfoService {

    @Autowired
    private BeltAnomalyInfoMapper baseMapper;

    @Autowired
    CacheService cacheService;

    @Resource
    FlowBasicFeign flowBasicFeign;

    @Resource
    BasicDeviceFeign basicDeviceFeign;

    @Resource
    IBeltRouteTaskDetailService routeTaskDetailService;

    @Resource
    IBeltPerformanceMinuteService beltPerformanceMinuteService;

    @Resource
    IBeltHealthScoreService beltHealthScoreService;

    @Resource
    IBeltRouteTaskMainService routeTaskMainService;

    @Resource
    IBeltProtectionStatusService beltProtectionStatusService;

    @Override
    public BeltAnomalyInfoRespVO queryById(Long id) {
        if (id == null) {
            return null;
        }
        BeltAnomalyInfo entity = baseMapper.selectById(id);
        return BeltAnomalyInfoAssembler.entity2dto(entity);
    }

    @Override
    public PageResponse<BeltAnomalyInfoRespVO> page(PageRequest<BeltAnomalyInfoReqDTO> request) {
        BeltAnomalyInfoReqDTO condition = request.getQueryCondition();
        if (condition != null && condition.getBeltName() != null) {
            BaseResponse<List<BasicDeviceRespVO>> list = basicDeviceFeign.list(new BasicDeviceReqDTO().setDeviceName(condition.getBeltName()));
            if (!list.getData().isEmpty()) {
                condition.setBeltId(list.getData().get(0).getId());
            }
        }
        Page<BeltAnomalyInfoRespVO> page = new Page<>(request.getPageIndex(), request.getPageSize());
        IPage<BeltAnomalyInfoRespVO> pageData = baseMapper.pageQuery(page, condition);
        List<BeltAnomalyInfoRespVO> records = pageData.getRecords();
        for (BeltAnomalyInfoRespVO record : records) {
            if (record.getBeltId() != null) {
                BasicDeviceRespVO basicDeviceRespVO = basicDeviceFeign.queryById(record.getBeltId()).getData();
                record.setBeltName(basicDeviceRespVO != null ? basicDeviceRespVO.getDeviceName() : null);
            }
        }
        return new PageResponse<>((Page<?>) pageData, pageData.getRecords());
    }

    @Override
    public List<BeltAnomalyInfoRespVO> list(BeltAnomalyInfoReqDTO reqDto) {
        return baseMapper.queryList(reqDto);
    }

    @Override
    public void add(BeltAnomalyInfoReqDTO reqDto) {
        BeltAnomalyInfo entity = BeltAnomalyInfoAssembler.dto2entity(reqDto);
        baseMapper.insert(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchAdd(List<BeltAnomalyInfoReqDTO> dtoList) {
        dtoList.forEach(this::add);
    }

    @Override
    public void update(BeltAnomalyInfoReqDTO reqDto) {
        BeltAnomalyInfo oldEntity = baseMapper.selectById(reqDto.getId());
        if (oldEntity == null) {
            throw new RecordNotFoundException();
        }
        BeltAnomalyInfo newEntity = BeltAnomalyInfoAssembler.dto2entity(reqDto, oldEntity);
        baseMapper.updateById(newEntity);
    }

    @Override
    public void delete(Long id) {
        BeltAnomalyInfo oldEntity = baseMapper.selectById(id);
        if (oldEntity == null) {
            throw new RecordNotFoundException();
        }
        baseMapper.deleteById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchDelete(List<Long> ids) {
        for (Long id : ids) {
            delete(id);
        }
    }

    @Override
    public List<BeltAnomalyInfo> todayAnomalyItem(Long id) {
        // 查询今日异常项
        LocalDateTime todayStart = LocalDate.now().atStartOfDay();
        LocalDateTime tomorrowStart = LocalDate.now().atStartOfDay().plusDays(1);
        return baseMapper.selectList(new LambdaQueryWrapper<BeltAnomalyInfo>().eq(BeltAnomalyInfo::getBeltId, id).between(BeltAnomalyInfo::getAnomalyTime, todayStart, tomorrowStart));
    }

    @Override
    public List<BeltAnomalyInfo> weekAnomalyItem(Long id) {
        // 查询本周异常项
        LocalDateTime todayStart = LocalDate.now().atStartOfDay().plusDays(1);
        LocalDateTime weekStart = todayStart.minusDays(6);
        return baseMapper.selectList(new LambdaQueryWrapper<BeltAnomalyInfo>().eq(BeltAnomalyInfo::getBeltId, id).between(BeltAnomalyInfo::getAnomalyTime, weekStart, todayStart));
    }

    @Override
    public List<BeltAnomalyInfo> monthAnomalyItem(Long id) {
        // 查询本月异常项
        LocalDateTime todayStart = LocalDate.now().atStartOfDay();
        LocalDateTime monthStart = todayStart.minusDays(30);
        return baseMapper.selectList(new LambdaQueryWrapper<BeltAnomalyInfo>().eq(BeltAnomalyInfo::getBeltId, id).between(BeltAnomalyInfo::getAnomalyTime, monthStart, todayStart));

    }

    @Override
    public void searchForRouteExceptions() {
        // 获取所有在运行中的路线任务键
        Set<String> keys = cacheService.getKeys(BeltCloudConstant.BC_RUNNING_ROUTE_KEY + "*");
        LocalDateTime now = LocalDateTime.now();  // 当前时间
        LocalDateTime tenMinutesAgo = now.minusMinutes(10);  // 十分钟前的时间
        if (keys.isEmpty()) {
            return;
        }
        // 遍历每个运行中的任务键
        for (String key : keys) {
            // 从缓存中获取路线任务数据
            BeltRouteBelongData routeData = JSONUtil.toBean(cacheService.getString(key),BeltRouteBelongData.class);
            // 根据任务ID获取任务详情
            BeltRouteTaskDetail taskDetail = getTaskDetail(routeData.getTaskId());

            // 如果任务详情为空，跳过当前任务
            if (taskDetail == null) {
                continue;
            }

            // 检查是否存在连续空载异常
            checkNoLoadAnomaly(routeData.getTaskId(), taskDetail);
            // 检查是否存在启停次数过多异常
            checkFrequentStartStop(routeData.getFlowNo(), taskDetail, tenMinutesAgo, now);
        }
    }

    /**
     * 根据任务ID获取任务详情
     * @param taskId 任务ID
     * @return BeltRouteTaskDetail 任务详情
     */
    private BeltRouteTaskDetail getTaskDetail(Long taskId) {
        return routeTaskDetailService.getOne(
                new LambdaQueryWrapper<BeltRouteTaskDetail>().eq(BeltRouteTaskDetail::getTaskId, taskId)
        );
    }

    /**
     * 检查路线是否存在连续空载异常
     * @param taskId 任务ID
     * @param taskDetail 任务详情
     */
    private void checkNoLoadAnomaly(Long taskId, BeltRouteTaskDetail taskDetail) {
        if (routeTaskDetailService.checkNoLoadTimeOut(taskId)) {
            // 插入空载异常记录
            insertAnomaly(taskDetail, "连续空载", "路线连续空载时间超过10分钟");
        }
    }

    /**
     * 检查路线是否存在启停次数过多异常
     * @param flowNo 路线唯一标志
     * @param taskDetail 任务详情
     * @param startTime 开始时间
     * @param endTime 结束时间
     */
    private void checkFrequentStartStop(Short flowNo, BeltRouteTaskDetail taskDetail, LocalDateTime startTime, LocalDateTime endTime) {
        // 统计指定时间范围内不重复的任务数量
        int taskCount = routeTaskMainService.numberOfUnduplicatedTasks(flowNo, startTime, endTime);
        if (taskCount >= 10) {
            // 插入启停次数过多异常记录
            insertAnomaly(taskDetail, "启停次数过多", "路线10分钟内的任务数量超过10次");
        }
    }

    /**
     * 插入异常信息记录
     * @param taskDetail 任务详情
     * @param anomalyItem 异常项名称
     * @param anomalyDesc 异常描述
     */
    private void insertAnomaly(BeltRouteTaskDetail taskDetail, String anomalyItem, String anomalyDesc) {
        BeltAnomalyInfo anomalyInfo = new BeltAnomalyInfo();
        anomalyInfo.setTaskId(taskDetail.getTaskId())  // 设置任务ID
                .setAnomalyTime(LocalDateTime.now())  // 设置异常时间
                .setRouteId(Long.valueOf(taskDetail.getFlowNo()))  // 设置路线ID
                .setResolved(0)  // 设置异常未解决状态
                .setAnomalyItem(anomalyItem)  // 设置异常项
                .setAnomalyDesc(anomalyDesc)  // 设置异常描述
                .setSuggestion("")  // 设置建议（为空）
                .setTaskType(taskDetail.getTaskType())  // 设置任务类型
                .setIsDeleted(0);  // 设置未删除状态
        // 插入异常记录到数据库
        baseMapper.insert(anomalyInfo);
    }


    @Override
    public void minuteInformationCheck(List<BeltPerformanceMinute> beltPerformanceMinuteList) {
        beltPerformanceMinuteList.forEach(beltPerformanceMinute -> {
          if (beltPerformanceMinute.getLoadStatus() == 1){ //负载状态
            //判断负载率
            beltHealthScoreService.determineTheNumericalRangeTypeAnomaly(beltPerformanceMinute,LOAD_FACTOR_CODE);
          }else {
            //判断空载负载率
            beltHealthScoreService.determineTheNumericalRangeTypeAnomaly(beltPerformanceMinute,NO_LOAD_FACTOR_CODE);
          }
          //判断阻力
          beltHealthScoreService.determineTheNumericalRangeTypeAnomaly(beltPerformanceMinute,Resistance_FACTOR_CODE);
        });
    }

    /**
     * 获取正在运行的任务相关的所有皮带设备信息
     */
    private List<BasicDeviceRespVO> getRunningBeltDevices() {
        // 获取正在运行中的所有任务键
        Set<String> keys = cacheService.getKeys(BeltCloudConstant.BC_RUNNING_ROUTE_KEY + "*");
        List<BasicDeviceRespVO> allBeltList = new ArrayList<>();
        if (keys.isEmpty()) {
            return allBeltList;
        }

        // 遍历每一个任务键获取其相关的皮带设备
        for (String key : keys) {
            // 从缓存中获取相关的任务属性
            BeltRouteBelongData beltRouteBelongData = JSONUtil.toBean(cacheService.getString(key),BeltRouteBelongData.class);

            // 根据任务流程号获取相关目录信息
            FlowBasicReqDTO flowBasicReqDTO = new FlowBasicReqDTO().setFlowNo(beltRouteBelongData.getFlowNo());
            FlowBasicRespVO flowBasicRespVO = flowBasicFeign.all(flowBasicReqDTO).getData().get(0);

            // 根据流程信息获取相关的皮带设备编码
            List<String> beltCodes = Arrays.asList(flowBasicRespVO.getFlowCode().split(StringPool.DASH));

            // 获取所有的皮带设备
            List<BasicDeviceRespVO> basicDeviceRespVOS = basicDeviceFeign.list(new BasicDeviceReqDTO()).getData();

            // 根据流程编码匹配相关的皮带设备
            for (BasicDeviceRespVO basicDeviceRespVO : basicDeviceRespVOS) {
                if (beltCodes.contains(basicDeviceRespVO.getDeviceCode())) {
                    allBeltList.add(basicDeviceRespVO);
                }
            }
        }
        return allBeltList;
    }

    @Override
    public void searchForBeltExceptions() {
        // 获取所有正在运行任务相关的皮带设备
        List<BasicDeviceRespVO> beltList = getRunningBeltDevices();

        // 遍历所有皮带设备
        beltList.forEach(basicDeviceRespVO -> {
            // 查询5分钟内皮带的分钟性能信息
            List<BeltPerformanceMinute> beltPerformanceMinuteList = beltPerformanceMinuteService.list(
                    new LambdaQueryWrapper<BeltPerformanceMinute>()
                            .eq(BeltPerformanceMinute::getBeltId, basicDeviceRespVO.getId())
                            .gt(BeltPerformanceMinute::getEndTime, LocalDateTime.now().minusMinutes(5))
            );

            // 进行相关的信息检查
            minuteInformationCheck(beltPerformanceMinuteList);
        });
    }

    @Override
    public void fiveDefenseCheck() {
        // 获取所有正在运行任务相关的皮带设备
        List<BasicDeviceRespVO> allBeltList = getRunningBeltDevices();

        // 根据皮带ID分组 查详30分钟内五防状态
        List<FiveDefenseCount> fiveDefenseCountList = beltProtectionStatusService.groupByBeltId(
                allBeltList, LocalDateTime.now().minusMinutes(30), LocalDateTime.now()
        );

        //检查五防状态
        fiveDefenseCountList.forEach(fiveDefenseCount -> {
            beltHealthScoreService.determineTheSignalTypeAnomaly(fiveDefenseCount, MINOR_DEVIATION_CODE);
            beltHealthScoreService.determineTheSignalTypeAnomaly(fiveDefenseCount, SEVERE_DEVIATION_CODE);
            beltHealthScoreService.determineTheSignalTypeAnomaly(fiveDefenseCount, ROPE_CODE);
            beltHealthScoreService.determineTheSignalTypeAnomaly(fiveDefenseCount, BLOCK_CODE);
            beltHealthScoreService.determineTheSignalTypeAnomaly(fiveDefenseCount, TEAR_CODE);
            beltHealthScoreService.determineTheSignalTypeAnomaly(fiveDefenseCount, SLIP_CODE);
        });
    }


}




